// Copyright 2008-2009 BUGCOLLECT.COM
// Author: Remus Rusanu
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//                 http://bugcollect.com/terms
//

package com.bugcollect;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.XMLStreamException;

/**
 * This class stores the information about a report submitted to bugcollect.com.
 */
public final class Report
{
      final static String tagNamespace = "http://bugcollect.com/schemas/report/v1.0";

      private UUID _uuid;
      /**
       * Sets the report global unique identifier.
       * A new random UUID is assigned to each <code>Report</code> in
       * the construnctor. Report UUIDs can be used to retry submittion
       * if bugcollect.com cannot be contacted.
       */
      public void setUUID(UUID value)
      {
          _uuid = value;
      }
      
      /**
       * Gets the report global unique identifier.
       */
      public UUID getUUID()
      {
          return _uuid;
      }

      private String _message;
      /**
       * Sets the report error message.
       * This property is part of the report bucket hash.
       */
      public void setMessage(String value)
      {
          _message = value;
      }

      /**
       * Gets the error message
       */
      public String getMessage()
      {
          return _message;
      }

      private String _exception;
      /**
       * Sets the exception class name.
       * This property is part of the report bucket hash.
       */
      public void setException(String value)
      {
          _exception = value;
      }

      /**
       * Gets the exception class name
       */
      public String getException()
      {
          return _exception;
      }

      private int _severity;
      /**
       * Sets the report severity.
       * bugcollect.com does not interpret the severity values in any way.
       * This property is part of the report bucket hash.
       */
      public void setSeverity(int value)
      {
          _severity = value;
      }
      /**
       * Gets the report severity.
       */
      public int getSeverity()
      {
          return _severity;
      }


      private String _stackTrace;
      /**
       * The call stack of the reported incident.
       * This is automatically filled with the exception call stack.
       * This property is part of the report bucket hash.
       */
      public void setStackTrace(String value)
      {
          _stackTrace = value;
      }
      /**
       * Gets the report stack trace.
       */
      public String getStackTrace()
      {
          return _stackTrace;
      }

      private String _notes;
      /**
       * Sets the report notes.
       * Any additional information that can be relevant to the
       * submitted report and varies with each individual report.
       * This property is not part of the report bucket hash.
       */
      public void setNotes(String value)
      {
          _notes = value;
      }
      /**
       * Gets the report notes.
       */
      public String getNotes()
      {
          return _notes;
      }

      private String _application;
      /**
       * Sets the application name.
       * This property is part of the report bucket hash.
       */
      public void setApplication(
              String value)
      {
          _application = value;
      }
      /**
       * Gets the application name.
       */
      public String getApplication()
      {
          return _application;
      }

      private String _appVersion;
      /**
       * Sets the application version.
       * This property is part of the report bucket hash.
       */
      public void setApplicationVersion(String value)
      {
          _appVersion = value;
      }
      /**
       * Gets the application version.
       */
      public String getApplicationVersion()
      {
          return _appVersion;
      }

      private String _module;
      /**
       * Sets the name of the module that is reporting the incident.
       * This property is part of the report bucket hash.
       */
      public void setModule(String value)
      {
          _module = value;
      }
      /**
       * Gets the module name.
       */
      public String getModule()
      {
          return _module;
      }

      private String _moduleVersion;
      /**
       * Sets the version of the module that is reporting the incident.
       * This property is part of the report bucket hash.
       */
      public void setModuleVersion(String value)
      {
          _moduleVersion = value;
      }
      /**
       * Gets the module version.
       */
      public String getModuleVersion()
      {
          return _moduleVersion;
      }

      private String _hostName;
      /**
       * The host name from where the report is submitted.
       * This is automatically filled from the network local hostname.
       */
      public void setHostName(String value)
      {
          _hostName = value;
      }
      /**
       * Gets the host name.
       */
      public String getHostName()
      {
          return _hostName;
      }

      private String _osName;
      /**
       * The host operating system name.
       * This value is automatically filled from the
       * System os.name property.
       * This property is not part of the report bucket hash.
        */
      public void setOs(String value)
      {
          _osName = value;
      }
      /**
       * Gets the OS name
       */
      public String getOs()
      {
          return _osName;
      }

      private String _osVersion;
     /**
       * The host operating system version.
       * This value is automatically filled from the
       * System os.version property.
       * This property is not part of the report bucket hash.
        */
       public void setOsVersion(String value)
      {
          _osVersion = value;
      }
       /**
        * Gets the OS version.
        */
      public String getOsVersion()
      {
          return _osVersion;
      }

      private Date _incidentTime;
      /**
       * The time of the reported incident.
       * This is automatically filled with the current time
       * when SubmitReport method is called.
       * This property is not part of the report bucket hash
       */
      public void setIncidentTime(Date value)
      {
          _incidentTime = value;
      }
      /**
       * Gets the incident time.
       */
      public Date getIncidentTime()
      {
          return _incidentTime;
      }

      private void initCommonFields()
      {
          _incidentTime = new Date();
          _osName = System.getProperty("os.name");
          _osVersion = System.getProperty("os.version");
          try
          {
            _hostName = java.net.Inet4Address.getLocalHost().getHostName();
          }
          catch(UnknownHostException uhe)
          {/*
              Logger.getLogger(ReportingManager.class.getName()).log(
                      Level.WARNING, "Exception trying to resove host name", uhe);
          */}
          _uuid = UUID.randomUUID();

      }

      /**
       * Creates a report.
       * The {@link #setIncidentTime incidentTime},
       * {@link #setOs os} and {@link #setOsVersion osVersion}
       * are initialized implicitly.
       */
      public Report()
      {
          initCommonFields();
      }

      /**
       * Creates a report.
       * Report fields are explicitly initialized
       * from the exception and notes information: {@link #setNotes notes},
       * {@link #setMessage message}, {@link #setException exception},
       * {@link #setSeverity severity}, {@link #setStackTrace stackTrace}
       * {@link #setIncidentTime incidentTime}, {@link #setOs os}
       * {@link #setOsVersion osVersion} and {@link #setHostName hostName}.
       */
      public Report(String notes, Throwable e)
      {
        _notes = notes;
        _message = e.getMessage();
        _exception = e.getClass().getName();
        _module = e.getStackTrace()[0].getClassName();
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        pw.flush();
        sw.flush();
        _stackTrace = sw.toString();
        initCommonFields();
      }

      private void WriteElement(
        XMLStreamWriter writer,
        String element,
        String value)
        throws XMLStreamException
      {
         if (null == value)
         {
            return;
         }
        writer.writeStartElement(element);
        writer.writeCharacters(value);
        writer.writeEndElement();
      }

      void writeToXmlStream(
        XMLStreamWriter writer)
        throws XMLStreamException
      {
          writer.writeStartElement("report");
          writer.writeDefaultNamespace(tagNamespace);
        WriteElement(writer, "report_uuid", _uuid.toString());
        WriteElement(writer, "message", _message);
        WriteElement(writer, "exception", _exception);
        WriteElement(writer, "severity", Integer.toString(_severity));
        WriteElement(writer, "stack", _stackTrace);
        WriteElement(writer, "notes", _notes);
        WriteElement(writer, "application", _application);
        WriteElement(writer, "application_version", _appVersion);
        WriteElement(writer, "module", _module);
        WriteElement(writer, "module_version", _moduleVersion);
        WriteElement(writer, "host_name", _hostName);
        WriteElement(writer, "os", _osName);
        WriteElement(writer, "os_version", _osVersion);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ssZ");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));

        WriteElement(writer, "incident_time", sdf.format(_incidentTime));
        writer.writeEndElement();
      }
}

